#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>

#include "../common/logger.h"

/*
Modify the program in Listing 30-1 ( thread_incr.c ) so that each loop in the thread’s
start function outputs the current value of glob and some identifier that uniquely
identifies the thread. The unique identifier for the thread could be specified as an
argument to the pthread_create() call used to create the thread. For this program,
that would require changing the argument of the thread’s start function to be a
pointer to a structure containing the unique identifier and a loop limit value. Run the
program, redirecting output to a file, and then inspect the file to see what happens to
glob as the kernel scheduler alternates execution between the two threads.
 */

struct thread_private_data {
        int loops;
        const char *identifier;
};

static int glob = 0;
static const char *log_file_name = "thread.log";

static void *thread_func(void *arg)
{
        struct thread_private_data data = *((struct thread_private_data *) arg);

        FILE *log_file = fopen(log_file_name, "a");
        if (log_file == NULL)
                log_system_error("fopen: ");

        char buffer[200];
        while (data.loops--) {
                int loc = glob;
                loc++;
                glob = loc;
                int bytes = sprintf(buffer, "%s: modified glob to %d\n",
                                    data.identifier, glob);
                if (bytes > 0) {
                        int wbytes =
                                fwrite(buffer, sizeof(char), bytes, log_file);
                        if (wbytes != bytes && ferror(log_file)) {
                                fclose(log_file);
                                log_system_error("ferror");
                        }
                } else {
                        fclose(log_file);
                        log_system_error("fwrite");
                }
        }
        fclose(log_file);

        return NULL;
}

int main(int argc, char *argv[])
{
        pthread_t thread_a, thread_b;

        int loops = (argc > 1) ? atoi(argv[1]) : 100;
        struct thread_private_data private_data_a = {
                loops, "thread_a"
        };
        struct thread_private_data private_data_b = {
                loops, "thread_b"
        };

        check_system_error(
                pthread_create(&thread_a, NULL, thread_func, &private_data_a),
                "pthread_create");
        check_system_error(
                pthread_create(&thread_b, NULL, thread_func, &private_data_b),
                "pthread_create");
        check_system_error(pthread_join(thread_a, NULL), "pthread_join");
        check_system_error(pthread_join(thread_b, NULL), "pthread_join");
        return EXIT_SUCCESS;
}
